/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bff.designtime.generator.core.rtgen.managergenerator.mgrImplclass.methodgenerators;


import com.inspur.edp.bff.designtime.generator.core.rtgen.managergenerator.mgrImplclass.FSMgrImplGenContext;
import com.inspur.edp.bff.designtime.generator.core.rtgen.typeinfo.FSDataClassTypeInfo;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.compileunit.CompileUnitGeneratorContext;
import com.inspur.edp.cef.api.message.CefException;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.dynamicProp.DynamicPropSetImpl;
import com.inspur.edp.cef.gencommon.MediateType;
import com.inspur.edp.cef.generator.coreGenerator.base.mgrGenerator.method.CreateDataCoreGenerator;
import com.inspur.edp.cef.generator.overall.GeneratorUtil;
import com.inspur.edp.cef.spi.common.UdtManagerUtil;
import com.inspur.edp.formserver.viewmodel.GspViewModel;
import com.inspur.edp.formserver.viewmodel.GspViewObject;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import org.eclipse.jdt.core.dom.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

public class CreateChildDataMethodGenerator extends CreateDataCoreGenerator {
    private static final String param_ChildCode = "childCode";
    private static final String var_Result = "data";
    private FSMgrImplGenContext mgrImpGenContext;
    private GspViewModel vo;
    public CreateChildDataMethodGenerator(FSMgrImplGenContext mgrImpGenContext) {
        super(mgrImpGenContext.getMgrGenContext());
        this.mgrImpGenContext = mgrImpGenContext;
        vo = mgrImpGenContext.getMgrGenContext().getFSGeneratorContext().getVmGenContext().getVm();
	}

    public static void addSetDynamicPropSetStatement(AST ast, Block block, IGspCommonField field,
                                                     CompileUnitGeneratorContext mgrImpGenContext) {
        MethodInvocation methodInvocation = ast.newMethodInvocation();
        methodInvocation.setName(ast.newSimpleName("set"+field.getLabelID()));
        methodInvocation.setExpression(ast.newSimpleName("data"));
        ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
        classInstanceCreation.setType(new TypeInfo(DynamicPropSetImpl.class).getType(ast, mgrImpGenContext));
        methodInvocation.arguments().add(classInstanceCreation);
        block.statements().add(ast.newExpressionStatement(methodInvocation));
    }

    private FSDataClassTypeInfo GetDataClassTypeInfo() {
        return mgrImpGenContext.getMgrGenContext().getFSGeneratorContext().getFsIntermediateTypeInfo().getFSDataInfo();
    }

    @Override
    protected Block buildMethodBody() {
        Block block = ast.newBlock();
        addBlockIfStatements(block);
        addThrowNotSupportChildCodeException(block);
        return block;
    }

    private void addBlockIfStatements(Block block) {
        GetDataClassTypeInfo().getChildObjDataTypes();
        for (Map.Entry<String, MediateType> item : GetDataClassTypeInfo().getChildObjDataTypes().entrySet()) {
            IfStatement ifStatement = ast.newIfStatement();
            addIfExpression(ifStatement, item);
            addIfThenStatement(ifStatement, item);
            block.statements().add(ifStatement);
        }
    }

    private void addIfExpression(IfStatement ifStatement, Map.Entry<String, MediateType> item) {
        MethodInvocation methodInvocation = ast.newMethodInvocation();
        methodInvocation.setName(ast.newSimpleName("equals"));
        methodInvocation.setExpression(ast.newSimpleName(param_ChildCode));
        StringLiteral stringLiteral = ast.newStringLiteral();
        stringLiteral.setLiteralValue(item.getKey());
        methodInvocation.arguments().add(stringLiteral);
        ifStatement.setExpression(methodInvocation);
    }

    private void addIfThenStatement(IfStatement ifStatement, Map.Entry<String, MediateType> item) {
        Block block = ast.newBlock();

        ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
        classInstanceCreation.setType(ast.newSimpleType(ast.newName(item.getValue().getClassName())));
        VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
        variableDeclarationFragment.setName(ast.newSimpleName(var_Result));
        variableDeclarationFragment.setInitializer(classInstanceCreation);
        VariableDeclarationStatement variableDeclarationStatement = ast.newVariableDeclarationStatement(variableDeclarationFragment);
        variableDeclarationStatement.setType(ast.newSimpleType(ast.newName(item.getValue().getClassName())));
        block.statements().add(variableDeclarationStatement);

        addSetProperties(block, item);

        ReturnStatement returnStatement = ast.newReturnStatement();
        returnStatement.setExpression(ast.newSimpleName(var_Result));
        block.statements().add(returnStatement);

        ifStatement.setThenStatement(block);
    }

    private void addSetProperties(Block block, Map.Entry<String, MediateType> item) {
        addSetNestedProperties(block, item);
    }

    //	}
    private void addSetNestedProperties(Block block, Map.Entry<String, MediateType> item) {
        String objectCode = item.getKey();
		GspViewObject obj;
		if("RootNode".equals(objectCode)){
			obj = vo.getMainObject();
		}else{
			obj = (GspViewObject)vo.findObjectByCode(objectCode);
		}

        for (IGspCommonField ele : obj.getContainElements()) {

        	if(ele.getIsUdt()){
				UnifiedDataTypeDef udt = mgrImpGenContext.getMgrGenContext().getFSGeneratorContext().getVmGenContext().getMetadataContent(ele.getUdtPkgName(), ele.getUdtID());
				addSetNestedPropertyStatement(objectCode, block, ele, udt);
			}else if(ele.getObjectType() == GspElementObjectType.DynamicProp){
				addSetDynamicPropSetStatement(ast, block, ele, mgrImpGenContext.getMgrGenContext().getRootCompileUnitGenCtx());

			}

        }
    }

    private void addSetNestedPropertyStatement(String objectCode, Block block, IGspCommonField ele, UnifiedDataTypeDef udt) {

        MethodInvocation methodInvocation = ast.newMethodInvocation();
        methodInvocation.setName(ast.newSimpleName("set"+ele.getLabelID()));
        methodInvocation.setExpression(ast.newSimpleName("data"));
        if (ele.getObjectType() != GspElementObjectType.Association || !ele.getIsUdt() || !ele.containRefElementNotFromAssoUdt()) {
            addCreateDataTypeStatement(methodInvocation, udt);
        } else {
            addCreateEnrichedAssoUdtStatement(methodInvocation, objectCode, ele);
        }
        block.statements().add(ast.newExpressionStatement(methodInvocation));
    }

    private void addCreateEnrichedAssoUdtStatement(MethodInvocation methodInvocation,
        String objectCode, IGspCommonField element) {
        ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
        classInstanceCreation.setType(ast.newSimpleType(ast.newName(
            mgrImpGenContext.getMgrGenContext().getFSGeneratorContext().getEntityGeneratorContext()
                .getEnrichedUdtAssoTypes().get(objectCode).get(element.getLabelID()).getFullName())));
        classInstanceCreation.arguments().add(ast.newNullLiteral());
        methodInvocation.arguments().add(classInstanceCreation);
    }

    private void addCreateDataTypeStatement(MethodInvocation methodInvocation, UnifiedDataTypeDef udt) {
        MethodInvocation methodInvocationCreateDataType = ast.newMethodInvocation();
        methodInvocationCreateDataType.setName(ast.newSimpleName("createDataType"));
        MethodInvocation methodInvocationCreateManager = ast.newMethodInvocation();
        methodInvocationCreateManager.setName(ast.newSimpleName("createManager"));
        methodInvocationCreateManager.arguments().add(GeneratorUtil.getStringLiteral(ast, udt.getUdtType()));
        addMethodCreateManagerExpression(methodInvocationCreateManager);

        methodInvocationCreateDataType.setExpression(methodInvocationCreateManager);

        CastExpression castExpression = ast.newCastExpression();
        castExpression.setType(ast.newSimpleType(ast.newName(udt.getGeneratedEntityClassInfo().getClassFullName())));
        castExpression.setExpression(methodInvocationCreateDataType);

        methodInvocation.arguments().add(castExpression);
    }

    private void addMethodCreateManagerExpression(MethodInvocation methodInvocationCreateManager) {
        MethodInvocation methodInvocation = ast.newMethodInvocation();
        methodInvocation.setName(ast.newSimpleName("getUdtFactory"));
        methodInvocation.setExpression(ast.newName(UdtManagerUtil.class.getTypeName()));

        methodInvocationCreateManager.setExpression(methodInvocation);
    }

    //======
    private void addThrowNotSupportChildCodeException(Block block) {
        addImport(CefException.class.getTypeName());
        ThrowStatement throwStatement = GeneratorUtil.getThrowStatement(ast,"不存在子表",param_ChildCode,true);
        block.statements().add(throwStatement);
    }

    @Override
    protected ArrayList<ParameterInfo> getParameterCollection() {
        ParameterInfo parameterInfo = new ParameterInfo();
        parameterInfo.setParamName("childCode");
        parameterInfo.setParamType(new TypeInfo(String.class));
        return new ArrayList<>(Arrays.asList(new ParameterInfo[]{parameterInfo}));
    }

    @Override
    protected TypeInfo getReturnType() {
        addImport(IEntityData.class.getTypeName());
        return new TypeInfo(IEntityData.class);
    }

    @Override
    protected String getMethodName() {
        return "innerCreateChildData";
    }

}
